package com.easipass.unifiedconfig;

import java.io.Serializable;
import java.util.regex.Pattern;

/**
 * Unified Configuration Identifier
 * @version $Id$
 * @author itea
 * @date 2010-1-22
 */
public final class UCI implements Serializable, Cloneable {

	private static final long serialVersionUID = 7662059176622689427L;
	private String domain;
	private String account;
	private String module;
	private String parameterName;
	
	public static final Pattern PATTERN_DOMAIN = Pattern.compile("[a-zA-Z_]\\w{0,19}|\\*");
	
	public static final Pattern PATTERN_ACCOUNT = Pattern.compile("\\.?\\w[\\w\\.\\-]{0,20}|\\*");
	
	public static final Pattern PATTERN_MODULE = Pattern.compile("[\\w/][/\\w\\.\\-]{0,39}|\\*");
	
	public static final Pattern PATTERN_PARAMETER_NAME = Pattern.compile("[a-zA-Z_\\.][\\w\\.\\-]{0,29}|\\*");
	
	public UCI(String uciStr) {
		String u = trimNull(uciStr);
		if(u == null) throw new NullPointerException("uci string is blank");
		
		int start = 0;
		int idx = -1;
		// int end = u.length();
		idx = u.indexOf(':');
		if(idx < 0) {// domain only
			setDomain(u);
			return;
		}
		String domain = u.substring(start, idx);
		if("".equals(domain)) throw new MalformedUCIException("missing domain: "+ uciStr);
		setDomain(domain);
		
		// to get account
		start = ++idx;
		idx = u.indexOf('[', start);
		if(idx < 0) // no module
			idx = u.indexOf('/', start);
		if(idx < 0) {// no parameter, either
			String a = u.substring(start);
			if("".equals(a)) throw new MalformedUCIException("missing account "+ uciStr);
			setAccount(a);
			return;
		} else {
			String a = u.substring(start, idx);
			if("".equals(a)) throw new MalformedUCIException("missing account "+ uciStr);
			setAccount(a);
		}
		
		// to get module
		start = idx;
		idx = u.indexOf('[', start);
		if(idx > 0) {
			start = ++idx;
			idx = u.indexOf(']', start);
			if(idx < 0)
				throw new MalformedUCIException("missing right parenthesis: ] "+ uciStr);
			String a = u.substring(start, idx);
			if("".equals(a)) throw new MalformedUCIException("missing module");
			setModule(a);
		}
		
		// to get parameter name
		start = ++idx;
		idx = u.indexOf('/', start);
		if(idx > 0) {
			String a = u.substring(idx+1);
			if("".equals(a)) throw new MalformedUCIException("missing parameter name "+ uciStr);
			setParameterName(a);
		}
	}

	public UCI(String domain, String account, String module,
			String parameterName) {
		this.setDomain(domain);
		this.setAccount(account);
		this.setModule(module);
		this.setParameterName(parameterName);
	}
	
	public UCI(UCI module, String parameterName) {
		this.setDomain(module.getDomain());
		this.setAccount(module.getAccount());
		this.setModule(module.getModule());
		this.setParameterName(parameterName);
	}
	
	public static UCI parser(String uci) {
		return new UCI(uci);
	}
	@Override
	public UCI clone() {
		return new UCI(domain, account, module, parameterName);
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append(domain);
		if(account != null)builder.append(":").append(account);
		if(module != null)builder.append("[").append(module).append("]");
		if(parameterName != null)builder.append("/").append(parameterName);
		return builder.toString();
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((account == null) ? 0 : account.hashCode());
		result = prime * result + ((module == null) ? 0 : module.hashCode());
		result = prime * result
				+ ((parameterName == null) ? 0 : parameterName.hashCode());
		result = prime * result
				+ ((domain == null) ? 0 : domain.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		UCI other = (UCI) obj;
		if (account == null) {
			if (other.account != null)
				return false;
		} else if (!account.equals(other.account))
			return false;
		if (module == null) {
			if (other.module != null)
				return false;
		} else if (!module.equals(other.module))
			return false;
		if (parameterName == null) {
			if (other.parameterName != null)
				return false;
		} else if (!parameterName.equals(other.parameterName))
			return false;
		if (domain == null) {
			if (other.domain != null)
				return false;
		} else if (!domain.equals(other.domain))
			return false;
		return true;
	}

	public boolean isWildcard() {
		return "*".equals(domain)
			|| "*".equals(account)
			|| "*".equals(module)
			|| "*".equals(parameterName);
	}
	public boolean obtains(UCI u) {
		return matchesDomain(u.getDomain())
			&& matchesAccount(u.getAccount())
			&& matchesModule(u.getModule())
			&& matchesParameter(u.getParameterName());
	}
	public boolean matchesDomain(String val) {
		String s = trimNull(val);
		if(domain == null)
			return s == null;
		else 
			return domain.equals("*") ||  domain.equalsIgnoreCase(s);
	}
	public boolean matchesAccount(String val) {
		String a = trimNull(val);
		if(this.account == null)
			return a == null;
		else 
			return account.equals("*") || account.equals(a);
	}
	public boolean matchesModule(String val) {
		String m = trimNull(val);
		if(module == null)
			return m == null;
		else
			return module.equals("*") || module.equals(m);
	}
	public boolean matchesParameter(String val) {
		String p = trimNull(val);
		if(parameterName == null)
			return p == null;
		else
			return parameterName.equals("*") || parameterName.equals(p);
	}
	public String getDomain() {
		return domain;
	}
	public String getAccount() {
		return account;
	}
	public String getModule() {
		return module;
	}
	public String getParameterName() {
		return parameterName;
	}

	private void setDomain(String domain) {
		this.domain = trimNull(domain);
		if(this.domain != null && !PATTERN_DOMAIN.matcher(this.domain).matches())
			throw new MalformedUCIException("domain is malformed: "+ domain);
		if(this.domain != null) this.domain = this.domain.toLowerCase();
	}

	private void setAccount(String account) {
		this.account = trimNull(account);
		if(this.account != null && !PATTERN_ACCOUNT.matcher(this.account).matches())
			throw new MalformedUCIException("account is malformed: "+ account);
	}

	private void setModule(String module) {
		this.module = trimNull(module);
		if(this.module != null && !PATTERN_MODULE.matcher(this.module).matches())
			throw new MalformedUCIException("module is malformed: "+ module);
	}

	private void setParameterName(String parameterName) {
		this.parameterName = trimNull(parameterName);
		if(this.parameterName != null && !PATTERN_PARAMETER_NAME.matcher(this.parameterName).matches())
			throw new MalformedUCIException("parameterName is malformed: "+ parameterName);
	}
	
	private static String trimNull(String s) {
		if(s == null) return null;
		return (s = s.trim()).length() == 0 ? null : s;
	}
}
